<pre class='metadata'>
Title: CSS Anchor Positioning Module Level 1
Shortname: css-anchor-position
Level: 1
Status: ED
Group: csswg
Work Status: exploring
ED: https://drafts.csswg.org/css-anchor-position-1/
TR: https://www.w3.org/TR/css-anchor-position-1/
Editor: Tab Atkins-Bittner, Google, http://xanthir.com/contact/, w3cid 42199
Editor: Elika J. Etemad / fantasai, Apple, http://fantasai.inkedblade.net/contact, w3cid 35400
Former Editor: Jhey Tompkins, Google, https://twitter.com/jh3yy, w3cid 137616
Editor: Ian Kilpatrick, Google, w3cid 73001
Abstract: This specification defines [=anchor positioning=],
	where a positioned element can size and position itself
	relative to one or more “anchor elements” elsewhere on the page.
Ignored Terms: cssText, transitions
</pre>

<pre class=link-defaults>
spec:css-backgrounds-3; type:property; text:border-color
spec:css-break-4; type:dfn; text:fragment
spec:css-conditional-5; type:dfn; text:container query
spec:css-display-3; type:dfn;
	text:element
	text:invisible
spec:css-position-3;
	type:dfn; text:inset-modified containing block
	type:property; text:inset-inline-start
spec:css-cascade-5; type:dfn; text:property
spec:dom; type:dfn; text:shadow tree
spec:css-align-3; type:value; text:center
spec:css-values-5; type:dfn; text:invalid at computed-value time
</pre>

<pre class=anchors>
spec:aria-1.3; type: element-attr; text: role; for: html-global; url: https://w3c.github.io/aria/#host_general_role
</pre>

<style>
/* Put nice boxes around each algorithm. */
[data-algorithm]:not(.heading) {
	padding: .5em;
	border: thin solid #ddd; border-radius: .5em;
	margin: .5em calc(-0.5em - 1px);
}
[data-algorithm]:not(.heading) > :first-child {
	margin-top: 0;
}
[data-algorithm]:not(.heading) > :last-child {
	margin-bottom: 0;
}
[data-algorithm] [data-algorithm] {
	margin: 1em 0;
}
</style>

Introduction {#intro}
=====================

CSS [=absolute positioning=] allows authors
to place boxes anywhere on the page,
without regard to the layout of other boxes
besides their containing block.
This flexibility can be very useful,
but also very limiting--
often you want to position relative to <em>some</em> other box.
<dfn export>Anchor positioning</dfn>
(via the 'position-anchor' and 'position-area' properties
and/or the <dfn export>anchor functions</dfn> ''anchor()'' and ''anchor-size()'')
allows authors to achieve this,
“anchoring” an [=absolutely positioned box=]
to one or more other boxes on the page
(its <dfn export lt="anchor reference">anchor references</dfn>,
while also allowing them to try several possible positions
to find the “best” one that avoids overlap/overflow.

<div class=example>
	For example, an author might want to position a tooltip
	centered and above the targeted element,
	unless that would place the tooltip offscreen,
	in which case it should be below the targeted element.
	This can be done with the following CSS:

	<pre class=lang-css>
	.anchor {
		anchor-name: --tooltip;
	}
	.tooltip {
		/* Fixpos means we don't need to worry about
		   containing block relationships;
		   the tooltip can live anywhere in the DOM. */
		position: fixed;

		/* All the anchoring behavior will default to
		   referring to the --tooltip anchor. */
		position-anchor: --tooltip;

		/* Align the tooltip's bottom to the top of the anchor;
		   this also defaults to horizontally center-aligning
		   the tooltip and the anchor (in horizontal writing modes). */
		position-area: block-start;

		/* Automatically swap if this overflows the window
		   so the tooltip's top aligns to the anchor's bottom
		   instead. */
		position-try: flip-block;

		/* Prevent getting too wide */
		max-inline-size: 20em;
	}
	</pre>

	Note that using the [[HTML#the-popover-attribute|Popover API]]
	will automatically set 'position' and create the anchoring relationship
	without setting 'anchor-name' or 'position-anchor' value
	(by defining an [=implicit anchor element=]),
	so those properties wouldn't need to be explicitly set again.
	So with the correct markup, this example can be simplified to:

	<pre class=lang-css>
	.tooltip {
		/* Using the popover + popovertarget attributes sets 'position: fixed'
		   and creates the necessary position-anchor relationship already. */
		position-area: block-start;
		position-try: flip-block;
		max-inline-size: 20em;
	}
	</pre>
</div>

<h3 id="values">
Value Definitions</h3>

This specification follows the <a href="https://www.w3.org/TR/CSS2/about.html#property-defs">CSS property definition conventions</a> from [[!CSS2]]
using the <a href="https://www.w3.org/TR/css-values-3/#value-defs">value definition syntax</a> from [[!CSS-VALUES-3]].
Value types not defined in this specification are defined in CSS Values &amp; Units [[!CSS-VALUES-3]].
Combination with other CSS modules may expand the definitions of these value types.

In addition to the property-specific values listed in their definitions,
all properties defined in this specification
also accept the <a>CSS-wide keywords</a> as their property value.
For readability they have not been repeated explicitly.

Like most operations in CSS besides selector matching,
features in this specification operate over the [=flattened element tree=].

Determining the Anchor {#determining}
======================

<!-- Big Text: a-name

 ███▌        █    █▌  ███▌  █     █ █████▌
▐█ ▐█        █▌   █▌ ▐█ ▐█  ██   ██ █▌    
█▌  █▌       ██▌  █▌ █▌  █▌ █▌█ █▐█ █▌    
█▌  █▌ ████▌ █▌▐█ █▌ █▌  █▌ █▌ █ ▐█ ████  
█████▌       █▌  ██▌ █████▌ █▌   ▐█ █▌    
█▌  █▌       █▌   █▌ █▌  █▌ █▌   ▐█ █▌    
█▌  █▌       █▌   ▐▌ █▌  █▌ █▌   ▐█ █████▌
-->

Creating an Anchor: the 'anchor-name' property {#name}
----------------------------------------------

<pre class=propdef>
Name: anchor-name
Value: none | <<dashed-ident>>#
Initial: none
Inherited: no
Applies to: all elements that generate a [=principal box=]
Animation Type: discrete
</pre>

The 'anchor-name' property declares
that an element is an <dfn>anchor element</dfn>,
whose [=principal box=] is an [=anchor box=],
and gives it a list of <dfn lt="anchor name">anchor names</dfn> to be targeted by.
Values are defined as follows:

<dl dfn-type=value dfn-for=anchor-name>
	: <dfn>none</dfn>
	:: The property has no effect.

	: <dfn><<dashed-ident>>#</dfn>
	:: If the element generates a [=principal box=],
		the element is an [=anchor element=],
		with a list of [=anchor names=] as specified.
		Each [=anchor name=] is a [=tree-scoped name/loosely matched=] [=tree-scoped name=].

		Otherwise, the property has no effect.
</dl>

[=Anchor names=] do not need to be unique.
Not all elements are capable of being
the [=target anchor element=] of a given box.
Thus a name can be reused in multiple places
if the usages are scoped appropriately.

Note: If multiple elements share an [=anchor name=]
and are all visible to a given positioned box,
the [=target anchor element=] will be the last one in DOM order.
The 'anchor-scope' property can be used to further limit
what names are visible to a given referencing box.

[=Anchor names=] are <em>not</em> scoped by [=containment=] by default;
even if an element has [=style containment|style=] or [=layout containment=]
(or any similar sort of containment),
the [=anchor names=] of its descendants are visible to elements elsewhere in the page.

Note: While an element is in the [=skipped contents=] of another element
(due to ''content-visibility: hidden'', for instance),
it's not an [=acceptable anchor element=],
effectively acting as if it had no names.

Note: Positioned elements in [=shadow trees=]
can reference [=anchor names=] defined in “higher” trees.
Currently, they cannot reference [=anchor names=]
defined in “lower” shadow trees, though.

### Implicit Anchor Elements ### {#implicit}

Some specifications can define that,
in certain circumstances,
a particular element is an <dfn>implicit anchor element</dfn>
for another element.

<p class=example>
	TODO: Fill in an example new popover-related details
	(once that finally lands in the HTML spec).

[=Implicit anchor elements=] can be referenced
with the ''position-anchor/auto'' keyword in 'position-anchor',
or by omitting the anchor reference in [=anchor functions=].

The [=implicit anchor element=] of a [=pseudo-element=]
is its [=originating element=],
unless otherwise specified.

### The Anchor Box ### {#anchor-position-size}

Several features of this specification refer to the position and size of an <dfn local-lt=anchor>anchor box</dfn>.
Unless otherwise specified,
this refers to the [=border box=] edge of the [=principal box=] of the [=anchor element=].
The [=anchor box=]'s position and size is determined after layout.

This position and size includes 'zoom' and 'position'-based adjustments
(such as ''position: relative'' or ''position: sticky'')
as well as transforms (such as 'transform' or 'offset-path').
In these cases, the axis-aligned bounding rectangle of the [=anchor box=]
in the coordinate space of the [=absolutely positioned=] element's [=containing block=]
is used instead.
Transforms are often optimized onto a different thread,
so transform-based updates to an [=anchor box’s=] position
may be delayed by a few frames.
Authors can avoid this delay by using absolute or relative positioning instead where practical.

If the [=anchor box=] is [=fragmented=],
and the [=containing block=] of the [=absolutely positioned=] box
referring to that [=anchor box=] is outside the relevant [=fragmentation context=],
the axis-aligned bounding rectangle of its [=box fragments=] is used instead.
(If the [=absolutely positioned=] box is inside the [=fragmentation context=],
it sees the [=anchor box=] as unfragmented--
and can be itself fragmented by the [=fragmentation context=].)

For performance reasons,
scrolling is handled specially, see [[#scroll]].
Other post-layout effects, such as filters,
do not affect the [=anchor box's=] position.


<!-- Big Text: a-scope

 ███▌         ███▌   ███▌   ███▌  ████▌  █████▌
▐█ ▐█        █▌  █▌ █▌  █▌ █▌  █▌ █▌  █▌ █▌    
█▌  █▌       █▌     █▌     █▌  █▌ █▌  █▌ █▌    
█▌  █▌ ████▌  ███▌  █▌     █▌  █▌ ████▌  ████  
█████▌           █▌ █▌     █▌  █▌ █▌     █▌    
█▌  █▌       █▌  █▌ █▌  █▌ █▌  █▌ █▌     █▌    
█▌  █▌        ███▌   ███▌   ███▌  █▌     █████▌
-->

<h3 id='anchor-scope'>
Scoping Anchor Names: the 'anchor-scope' property</h3>

<pre class=propdef>
Name: anchor-scope
Value: none | all | <<dashed-ident>>#
Initial: none
Applies to: all elements
Inherited: no
Animation type: discrete
Computed value: as specified
</pre>

This property scopes the specified [=anchor names=],
and lookups for these [=anchor names=],
to this element's subtree.
See [[#determining]].

Values have the following meanings:

<dl dfn-for="anchor-scope" dfn-type=value>
	<dt><dfn>none</dfn>
	<dd>
		No changes in [=anchor name=] scope.

	<dt><dfn>all</dfn>
	<dd>
		Specifies that all [=anchor names=] defined by this element or its descendants--
		whose scope is not already limited by a descendant using 'anchor-scope'--
		to be in scope only for this element's descendants;
		and limits descendants to only match [=anchor names=]
		to [=anchor elements=] within this subtree.

		This value only affects [=anchor names=] in the same tree scope,
		as if it were a [=tree-scoped name/strictly matched=] [=tree-scoped name=].
		(That is, ''anchor-scope: all'' acts identically
		to ''anchor-scope: --foo, --bar, ...'',
		listing all relevant [=anchor names=].)

	<dt><dfn><<dashed-ident>></dfn>
	<dd>
		Specifies that a matching [=anchor name=] defined by this element or its descendants--
		whose scope is not already limited by a descendant using 'anchor-scope'--
		to be in scope only for this element's descendants;
		and limits descendants to only match these [=anchor names=]
		to [=anchor elements=] within this subtree.

		The <<dashed-ident>> represents a [=tree-scoped name/strictly matched=] [=tree-scoped name=],
		i.e. it can only match against [=anchor names=] in the same shadow tree.[[!CSS-SCOPING-1]]
</dl>

This property has no effect on [=implicit anchor elements=].

<div class=example>
	When a design pattern is re-used,
	'anchor-scope' can prevent naming clashes across identical components.
	For example, if a list contains positioned elements within each list item,
	which want to position themselves relative to the list item they're in,


	<pre class=lang-css>
		li {
			anchor-name: --list-item;
			anchor-scope: --list-item;
		}
		li .positioned {
			position: absolute;
			position-anchor: --list-item;
			position-area: inline-start;
		}
	</pre>

	Without 'anchor-scope',
	all of the <{li}> elements would be visible
	to all of the positioned elements,
	and so they'd all positioned themselves relative to the <em>final</em> <{li}>,
	stacking up on top of each other.
</div>

<!-- Big Text: lookup

█▌     ███▌   ███▌  █▌  █▌ █▌  █▌ ████▌ 
█▌    █▌  █▌ █▌  █▌ █▌ █▌  █▌  █▌ █▌  █▌
█▌    █▌  █▌ █▌  █▌ █▌█▌   █▌  █▌ █▌  █▌
█▌    █▌  █▌ █▌  █▌ ██     █▌  █▌ ████▌ 
█▌    █▌  █▌ █▌  █▌ █▌█▌   █▌  █▌ █▌    
█▌    █▌  █▌ █▌  █▌ █▌ █▌  █▌  █▌ █▌    
█████  ███▌   ███▌  █▌  █▌  ███▌  █▌    
-->

Finding an Anchor {#target}
-----------------

Several things in this specification
find a [=target anchor element=],
given an <dfn>anchor specifier</dfn>,
which is either a <<dashed-ident>>
(and a [=tree-scoped reference=])
that should match an 'anchor-name' value elsewhere on the page,
or the keyword ''position-anchor/auto'',
or nothing (a missing specifier).

Note: The general rule captured by these conditions
is that an element can only be a positioned box's [=target anchor element=]
if its own box is fully laid out
before the positioned box that wants to reference it
is laid out.
CSS's layout rules provide some useful guarantees about this,
depending on the anchor and positioned box's relationship
with each other and their containing blocks.
The list of conditions below
exactly rephrases the stacking context rules
into just what's relevant for this purpose,
ensuring there is no possibility of circularity
in anchor positioning.

<div algorithm>
	To determine the <dfn>target [=anchor element=]</dfn>
	given a querying element |query el|
	and an optional [=anchor specifier=] |anchor spec|:

	1. If |anchor spec| was not passed,
		return the [=default anchor element=] if it exists,
		otherwise return nothing.

	2. If |anchor spec| is ''position-anchor/auto'':
		1. If |query el| has an [=implicit anchor element=]
			that is an [=acceptable anchor element=],
			return that element.

		2. Otherwise, return nothing.

		Note: Future APIs might also define implicit anchor elements.
		When they do, they'll be explicitly handled in this algorithm,
		to ensure coordination.

	3. Otherwise, |anchor spec| is a <<dashed-ident>>.
		Return the last element |el| in tree order
		that satisfies the following conditions:

		* |el| is an [=anchor element=] with an [=anchor name=] of |anchor spec|.

		* |el|'s [=anchor name=] [=tree-scoped name/loosely matched|loosely matches=] |anchor spec|.

			Note: The [=anchor name=] is a [=tree-scoped name=],
			while |anchor spec| is a [=tree-scoped reference=].

		* |el| is an [=acceptable anchor element=] for |query el|.

		If no element satisfies these conditions,
		return nothing.

		Note: 'anchor-scope' can restrict the visibility
		of certain [=anchor names=],
		which can affect what elements can be [=anchor elements=]
		for a given lookup.

	Note: An 'anchor-name' defined by styles in one [=shadow tree=]
	won't be seen by [=anchor functions=] in styles in a different [=shadow tree=],
	preserving encapsulation.
	However, <em>elements</em> in different [=shadow trees=]
	can still anchor to each other,
	so long as both the 'anchor-name' and [=anchor function=]
	come from styles in the same tree,
	such as by using ''::part()'' to style an element inside a shadow.
	([=Implicit anchor elements=] also aren't intrinsically limited to a single tree,
	but the details of that will depend on the API assigning them.)
</div>

<div algorithm="acceptable anchor element">
	An element |possible anchor| is an <dfn export>acceptable anchor element</dfn>
	for an [=absolutely positioned=] element |positioned el|
	if all of the following are true:

	* |possible anchor| is either an [=element=]
		or a fully styleable [=tree-abiding pseudo-element=].

	* |possible anchor| is in scope for |positioned el|,
		per the effects of 'anchor-scope' on |possible anchor|
		or its ancestors.

	* |possible anchor| is laid out strictly before |positioned el|,
		aka one of the following is true:
		* |possible anchor| and |positioned el| have the same [=original containing block=]
			and either
			* |possible anchor| is [=in a lower top layer=] than |positioned el|, or
			* they both exist in the same [=top layer=],
				but |possible anchor| is either not [=absolutely positioned=]
				or occurs earlier in the [=flat tree=] order than |positioned el|
		* The element generating |possible anchor|'s [=containing block=]
			(if one exists)
			is an [=acceptable anchor element=] for |positioned el|

	* If |possible anchor| is in the [=skipped contents=] of another element,
		then |positioned el| is in the [=skipped contents=] of that same element.

		Note: In other words, |positioned el| can anchor to |possible anchor| 
		if they're both in the same skipped "leaf",
		but it can't anchor "across" leafs.
		This means skipping an element that contains both of them
		won't suddenly cause the |positioned el| to move to another anchor,
		but still prevents positioned elements <em>elsewhere</em> in the page
		from anchoring to the skipped element.
</div>

<!-- Big Text: pos-anchor

████▌   ███▌   ███▌         ███▌  █    █▌  ███▌  █▌  █▌  ███▌  ████▌ 
█▌  █▌ █▌  █▌ █▌  █▌       ▐█ ▐█  █▌   █▌ █▌  █▌ █▌  █▌ █▌  █▌ █▌  █▌
█▌  █▌ █▌  █▌ █▌           █▌  █▌ ██▌  █▌ █▌     █▌  █▌ █▌  █▌ █▌  █▌
████▌  █▌  █▌  ███▌  ████▌ █▌  █▌ █▌▐█ █▌ █▌     █████▌ █▌  █▌ ████▌ 
█▌     █▌  █▌     █▌       █████▌ █▌  ██▌ █▌     █▌  █▌ █▌  █▌ █▌▐█  
█▌     █▌  █▌ █▌  █▌       █▌  █▌ █▌   █▌ █▌  █▌ █▌  █▌ █▌  █▌ █▌ ▐█ 
█▌      ███▌   ███▌        █▌  █▌ █▌   ▐▌  ███▌  █▌  █▌  ███▌  █▌  █▌
-->

<h3 id=position-anchor>
Default Anchors: the 'position-anchor' property</h3>

<pre class=propdef>
Name: position-anchor
Value: auto | <<anchor-name>>
Initial: auto
Applies to: [=absolutely positioned boxes=]
Inherited: no
Animation type: discrete
</pre>

The 'position-anchor' property specifies the <dfn>default anchor element</dfn>,
which is used by 'position-area', 'position-try',
and (by default) all [=anchor functions=] applied to this element.
'position-anchor' is a [=reset-only sub-property=] of 'position'.

<dl dfn-for=position-anchor dfn-type=value>

	: <dfn>auto</dfn>
	::
		Use the [=implicit anchor element=] if it exists;
		otherwise the box has no [=default anchor element=].

	: <dfn><<anchor-name>></dfn>
	::
		The [=target anchor element=] selected by the specified <<anchor-name>>
		is the box's [=default anchor element=].
</dl>

The [=principal box=] of the [=default anchor element=]
is the box's <dfn>default anchor box</dfn>.

<div class=example>
	For example, in the following code
	both ''.foo'' and ''.bar'' elements
	can use the same positioning properties,
	just changing the anchor element they're referring to:

	<pre highlight=css>
	.anchored {
		position: absolute;
		top: calc(.5em + anchor(outside));
		/* Since no anchor name was specified,
		   this automatically refers to the
		   default anchor box. */
	}

	.foo.anchored {
		position-anchor: --foo;
	}
	.bar.anchored {
		position-anchor: --bar;
	}
	</pre>
</div>

<h3 id=anchor-relevance>
Anchor Relevance</h3>

When determining whether an element |el| is [=relevant to the user=],
if a descendant of |el| is a [=target anchor element=]
for a positioned box
(which itself is not [=skipped contents|skipped=]
and whose [=containing block=] is not |el|
or a descendant of |el|),
then |el| must be considered [=relevant to the user=].

Note: This means that, for example,
an anchor in a ''content-visibility: auto'' subtree
will prevent its subtree from [=skipping its contents=]
as long as the positioned box relying on it
is also not [=skipped contents|skipped=].
(Unless the anchor and the positioned box
are both under the same ''content-visibility: auto'' element;
they can't cyclicly keep each other visible.)


Anchor-Based Positioning {#positioning}
========================

An [=absolutely positioned box=]
can position itself relative to one or more [=anchor boxes=] on the page.

The 'position-area' property offers a convenient grid-based concept
for positioning relative to the [=default anchor box=];
for more complex positioning or positioning relative to multiple boxes,
the ''anchor()'' function can be used in the [=inset properties=]
to explicitly refer to edges of an [=anchor box=].

<!-- Big Text: pos-area

████▌   ███▌   ███▌         ███▌  ████▌  █████▌  ███▌ 
█▌  █▌ █▌  █▌ █▌  █▌       ▐█ ▐█  █▌  █▌ █▌     ▐█ ▐█ 
█▌  █▌ █▌  █▌ █▌           █▌  █▌ █▌  █▌ █▌     █▌  █▌
████▌  █▌  █▌  ███▌  ████▌ █▌  █▌ ████▌  ████   █▌  █▌
█▌     █▌  █▌     █▌       █████▌ █▌▐█   █▌     █████▌
█▌     █▌  █▌ █▌  █▌       █▌  █▌ █▌ ▐█  █▌     █▌  █▌
█▌      ███▌   ███▌        █▌  █▌ █▌  █▌ █████▌ █▌  █▌
-->

The 'position-area' Property {#position-area}
---------------------------------------

<pre class=propdef>
Name: position-area
Value: none | <<position-area>>
Initial: none
Inherited: no
Applies to: positioned boxes with a [=default anchor box=]
Animation type: TBD
Computed value: the keyword ''position-area/none'' or a pair of keywords, see [[#position-area-computed]]
</pre>

Most common use-cases of [=anchor positioning=]
are only concerned with
the edges of the positioned box's [=containing block=]
and the edges of the [=default anchor box=].
These lines can be thought of as defining a 3×3 grid;
'position-area' lets you easily specify
what area of this [=position-area grid=] to lay out the positioned box in.

<figure>
	<img src="images/position-area-example.png" width=400>
	<figcaption>
		An example of ''position-area: top left'' positioning
		in a ''horizontal-tb'' ''dir/ltr'' [=writing mode=].
	</figcaption>
</figure>


<dl dfn-for=position-area dfn-type=value>
	: <dfn>none</dfn>
	:: The property has no effect.

	: <dfn><<position-area>></dfn>
	::
		If the box does not have a [=default anchor box=],
		or is not an [=absolutely positioned box=],
		this value has no effect.

		Otherwise, selects a region of the [=position-area grid=],
		and makes that the box's [=containing block=].

		Note: This means that the [=inset properties=] specify offsets from the position-area,
		and some property values,
		like ''max-height: 100%'',
		will be relative to the position-area as well.
</dl>

Values other than ''position-area/none'' have the following additional effects:

* The [=scrollable containing block=] is used in place of the [=local containing block=]
	when the [=absolute-position containing block=] is generated by a [=scroll container=],
	so that the entire [=scrollable overflow area=] (typically) is available
	for positioning.
* The [=used value=] of any ''top/auto'' [=inset properties=]
	and ''margin/auto'' [=margin properties=]
	resolves to ''0''.
* The ''align-self/normal'' value for the [=self-alignment properties=]
	resolves to a corresponding value, see [[#position-area-alignment]].

<h4 id="position-area-grid-resolution">
Resolving the Position Area Grid</h4>

The <dfn export>position-area grid</dfn> is a 3×3 grid,
composed of four grid lines in each axis.
In order (using the [=writing mode=] of the [=containing block=]):

* the [=start=] edge of the box's pre-modification [=containing block=],
	or the start edge of the [=default anchor box=]
	if that is more [=start=]-ward
* the [=start=] edge of the [=default anchor box=]
* the [=end=] edge of the [=default anchor box=]
* the [=end=] edge of the box's pre-modification [=containing block=],
	or the end edge of the [=default anchor box=]
	if that is more [=end=]-ward.

Note: When the [=default anchor box=]
is partially or completely outside of the pre-modified [=containing block=],
some of the [=position-area grid's=] rows or columns can be zero-sized.

<h4 id="position-area-syntax">
Syntax of <<position-area>> Values</h4>

Positions are specified as a pair of values,
which can be expressed in [=flow-relative=] or [=physical=] terms.
The allowed syntax of a <<position-area>> value is:

<pre class=prod>
<dfn>&lt;position-area></dfn> = [
	[ left | center | right | span-left | span-right
	| x-start | x-end | span-x-start | span-x-end
	| self-x-start | self-x-end | span-self-x-start | span-self-x-end
	| span-all ]
	||
	[ top | center | bottom | span-top | span-bottom
	| y-start | y-end | span-y-start | span-y-end
	| self-y-start | self-y-end | span-self-y-start | span-self-y-end
	| span-all ]
|
	[ block-start | center | block-end | span-block-start | span-block-end | span-all ]
	||
	[ inline-start | center | inline-end | span-inline-start | span-inline-end
	| span-all ]
|
	[ self-block-start | center | self-block-end | span-self-block-start
	| span-self-block-end | span-all ]
	||
	[ self-inline-start | center | self-inline-end | span-self-inline-start
	| span-self-inline-end | span-all ]
|
	[ start | center | end | span-start | span-end | span-all ]{1,2}
|
	[ self-start | center | self-end | span-self-start | span-self-end | span-all ]{1,2}
]
</pre>

The <<position-area>> value selects a region of the [=position-area grid=]
by specifying the rows and columns the region occupies as follows:

<dl dfn-type=value dfn-for="position-area, <position-area>">
	: <dfn>start</dfn>, <dfn>end</dfn>, <dfn>self-start</dfn>, <dfn>self-end</dfn>
	: <dfn>top</dfn>, <dfn>bottom</dfn>, <dfn>left</dfn>, <dfn>right</dfn>
	: <dfn>y-start</dfn>, <dfn>y-end</dfn>, <dfn>self-y-start</dfn>, <dfn>self-y-end</dfn>
	: <dfn>x-start</dfn>, <dfn>x-end</dfn>, <dfn>self-x-start</dfn>, <dfn>self-x-end</dfn>
	: <dfn>block-start</dfn>, <dfn>block-end</dfn>, <dfn>self-block-start</dfn>, <dfn>self-block-end</dfn>
	: <dfn>inline-start</dfn>, <dfn>inline-end</dfn>, <dfn>self-inline-start</dfn>, <dfn>self-inline-end</dfn>
	: <dfn>center</dfn>
	:: The single corresponding row or column,
		depending on which axis this keyword is specifying.

		Like in ''anchor()'',
		the plain logical keywords
		(''position-area/start'', ''position-area/end'', etc)
		refer to the [=writing mode=] of the box's [=containing block=].
		The ''position-area/x-start''/etc determine their direction in the same way,
		but in the specified physical axis.

		The ''self-*'' logical keywords
		(''position-area/self-start'', ''position-area/self-x-end'', etc)
		are identical,
		but refer to the box's own [=writing mode=].

	: <dfn>span-start</dfn>, <dfn>span-end</dfn>, <dfn>span-self-start</dfn>, <dfn>span-self-end</dfn>
	: <dfn>span-top</dfn>, <dfn>span-bottom</dfn>, <dfn>span-left</dfn>, <dfn>span-right</dfn>
	: <dfn>span-y-start</dfn>, <dfn>span-y-end</dfn>, <dfn>span-self-y-start</dfn>, <dfn>span-self-y-end</dfn>
	: <dfn>span-x-start</dfn>, <dfn>span-x-end</dfn>, <dfn>span-self-x-start</dfn>, <dfn>span-self-x-end</dfn>
	: <dfn>span-block-start</dfn>, <dfn>span-block-end</dfn>, <dfn>span-self-block-start</dfn>, <dfn>span-self-block-end</dfn>
	: <dfn>span-inline-start</dfn>, <dfn>span-inline-end</dfn>, <dfn>span-self-inline-start</dfn>, <dfn>span-self-inline-end</dfn>
	:: Two adjacent rows or columns,
		depending on which axis this keyword is specifying:
		the center row/column,
		and the row/column corresponding to the other half of the keyword
		as per the single-track keywords.

		(For example, ''span-top'' spans the first two rows--
		the center row and the top row.)

	: <dfn>span-all</dfn>
	:: All three rows or columns,
		depending on which axis this keyword is specifying.
</dl>

Some keywords are ambiguous about what axis they refer to:
''position-area/center'', ''position-area/span-all'',
and the ''position-area/start''/etc keywords that don't specify the block or inline axis explicitly.
If the other keyword is unambiguous about its axis,
then the ambiguous keyword is referring to the opposite axis.
(For example, in ''block-start center'',
the ''position-area/center'' keyword is referring to the inline axis.)
If both keywords are ambiguous, however,
then the first refers to the [=block axis=] of the box's [=containing block=],
and the second to the [=inline axis=].
(For example, ''span-all start'' is equivalent to ''span-all inline-start''.)

If only a single keyword is given,
it behaves as if the second keyword is ''position-area/span-all''
if the given keyword is unambigous about its axis;
otherwise, it behaves as if the given keyword was repeated.
(For example, ''position-area/top'' is equivalent to ''top span-all'',
but ''position-area/center'' is equivalent to ''center center''.)

<h4 id=position-area-computed>
Computed Value and Serialization of <<position-area>></h4>

The [=computed value=] of a <<position-area>> value is
the two keywords indicating the selected tracks in each axis,
with the long (''position-area/block-start'') and short (''position-area/start'') logical keywords
treated as equivalent.
It serializes in the order given in the grammar (above),
with the logical keywords serialized in their short forms
(e.g. ''start start'' instead of ''block-start inline-start'').

<!-- Big Text: anchor()

 ███▌  █    █▌  ███▌  █▌  █▌  ███▌  ████▌    ██ ██  
▐█ ▐█  █▌   █▌ █▌  █▌ █▌  █▌ █▌  █▌ █▌  █▌  █▌   ▐█ 
█▌  █▌ ██▌  █▌ █▌     █▌  █▌ █▌  █▌ █▌  █▌ █▌     ▐█
█▌  █▌ █▌▐█ █▌ █▌     █████▌ █▌  █▌ ████▌  █▌     ▐█
█████▌ █▌  ██▌ █▌     █▌  █▌ █▌  █▌ █▌▐█   █▌     ▐█
█▌  █▌ █▌   █▌ █▌  █▌ █▌  █▌ █▌  █▌ █▌ ▐█   █▌   ▐█ 
█▌  █▌ █▌   ▐▌  ███▌  █▌  █▌  ███▌  █▌  █▌   ██ ██  
-->

Anchor-relative Insets: the ''anchor()'' function {#anchor-pos}
-------------------------------------------------

An [=absolutely positioned box=]
can use the <dfn>anchor()</dfn> function
as a value in its [=inset properties=]
to refer to the position of one or more [=anchor boxes=].
The ''anchor()'' function resolves to a <<length>>.
It is only allowed in the [=inset properties=]
(and is otherwise invalid).

<pre class="propdef partial">
Name: top, left, right, bottom
New values: <<anchor()>>
</pre>

<pre class=prod>
	&lt;anchor()> = anchor( <<anchor-name>>? && <<anchor-side>>, <<length-percentage>>? )
	<dfn><<anchor-name>></dfn> = <<dashed-ident>>
	<dfn><<anchor-side>></dfn> = inside | outside
			   | top | left | right | bottom
			   | start | end | self-start | self-end
			   | <<percentage>> | center
</pre>

The ''anchor()'' function has three arguments:

* the <<anchor-name>> value
	specifies how to find the [=anchor element=]
	it will be drawing positioning information from.
	Its possible values are:

	<dl dfn-type=value dfn-for="anchor()">
		: <dfn><<dashed-ident>></dfn>
		:: Specifies the [=anchor name=] it will look for.
			This name is a [=tree-scoped reference=].

		: omitted
		:: Selects the [=default anchor element=]
			defined for the box,
			if possible.
	</dl>

	See [=target anchor element=] for details.

* the <<anchor-side>> value
	refers to the position of the corresponding side
	of the [=target anchor element=].
	Its possible values are:

	<dl dfn-type=value dfn-for="anchor()">
		: <dfn>inside</dfn>
		: <dfn>outside</dfn>
		:: Resolves to one of the [=anchor box's=] sides,
			depending on which [=inset property=] it's used in.
			''anchor()/inside'' refers to the same side as the [=inset property=]
			(attaching the positioned box to the "inside" of the [=anchor box=]),
			while ''anchor()/outside'' refers to the opposite.

		: <dfn>top</dfn>
		: <dfn>right</dfn>
		: <dfn>bottom</dfn>
		: <dfn>left</dfn>
		:: Refers to the specified side of the [=anchor box=].

			Note: These are only usable in the [=inset properties=]
			in the matching axis.
			For example, ''anchor()/left'' is usable in 'left', 'right',
			or the logical [=inset properties=] that refer to the horizontal axis.

		: <dfn>start</dfn>
		: <dfn>end</dfn>
		: <dfn>self-start</dfn>
		: <dfn>self-end</dfn>
		:: Refers to one of the sides of the [=anchor box=]
			in the same axis as the [=inset property=] it's used in,
			by resolving the keyword against the [=writing mode=]
			of either the positioned box
			(for ''anchor()/self-start'' and ''anchor()/self-end'')
			or the positioned box's containing block
			(for ''anchor()/start'' and ''anchor()/end'').

		: <dfn><<percentage>></dfn>
		: <dfn>center</dfn>
		:: Refers to a position
			a corresponding percentage between the ''anchor()/start'' and ''anchor()/end'' sides,
			with ''0%'' being equivalent to ''anchor()/start''
			and ''100%'' being equivalent to ''anchor()/end''.

			''anchor()/center'' is equivalent to ''50%''.

* the optional <<length-percentage>> final argument is a fallback value,
	specifying what the function should compute to
	if it's an [=unresolvable anchor function=].

An ''anchor()'' function representing a [=resolvable anchor function=]
resolves at [=computed value=] time
(using [=style & layout interleaving=])
to the <<length>> that would align the edge
of the positioned boxes' [=inset-modified containing block=]
corresponding to the property the function appears in
with the specified edge
of the [=target anchor element's=] [=anchor box=].

Note: This means that [=transitions=] or [=animations=]
of a property using an [=anchor function=]
will work "as expected" for all sorts of possible changes:
the [=anchor box=] moving,
[=anchor elements=] being added or removed from the document,
the 'anchor-name' property being changed on anchors,
etc.

<div class=example>
	For example,
	in ''.bar { inset-block-start: anchor(--foo block-start); }'',
	the ''anchor()'' will resolve to the length
	that'll line up the <code>.bar</code> element's block-start edge
	with the ''--foo'' anchor's block-start edge.

	On the other hand,
	in ''.bar { inset-block-end: anchor(--foo block-start); }'',
	it will instead resolve to the length
	that'll line up the <code>.bar</code> element's <em>block-end</em> edge
	with the ''--foo'' anchor's block-start edge.

	Since 'inset-block-start' and 'inset-block-end' values specify insets from different edges
	(the block-start and block-end of the element's [=containing block=],
	respectively),
	the same ''anchor()'' will usually resolve to different lengths in each.
</div>

<div class=example>
	Because the ''anchor()'' function resolves to a <<length>>,
	it can be used in [=math functions=] like any other length.

	ISSUE(10776): Add a better example; this one can be accomplished easily with ''align-self/anchor-center''.

	For example, the following will set up the element
	so that its [=inset-modified containing block=]
	is centered on the [=anchor box=]
	and as wide as possible without overflowing the [=containing block=]:

	<pre highlight=css>
	.centered-message {
		position: fixed;
		max-width: max-content;
		justify-self: center;

		--center: anchor(--x 50%);
		--half-distance: min(
			abs(0% - var(--center)),
			abs(100% - var(--center))
		);
		left: calc(var(--center) - var(--half-distance));
		right: calc(var(--center) - var(--half-distance));
		bottom: anchor(--x top);
	}
	</pre>

	This might be appropriate for an error message
	on an <{input}> element,
	for example,
	as the centering will make it easier to discover
	which input is being referred to.
</div>

<h4 id=anchor-resolution oldids=anchor-valid>
Resolution of ''anchor()''</h4>

An ''anchor()'' function is a
<dfn lt="resolvable anchor function|unresolvable anchor function">resolvable anchor function</dfn>
only if all the following conditions are true:

* It's applied to an [=absolutely positioned box=].
* If its <<anchor-side>> specifies a physical keyword,
	it's specified in an [=inset property=] applicable to that axis.
	(For example, ''anchor()/left'' can only be used in 'left', 'right',
	or a logical [=inset property=] in the horizontal axis.)
* There is a [=target anchor element=]
	for the box it's used on,
	and the <<anchor-name>> value specified in the function.

If any of these conditions are false,
the ''anchor()'' function [=computed value|computes=] to its specified fallback value.
If no fallback value is specified,
it makes the declaration referencing it [=invalid at computed-value time=].

<!-- Big Text: scroll

 ███▌   ███▌  ████▌   ███▌  █▌    █▌   
█▌  █▌ █▌  █▌ █▌  █▌ █▌  █▌ █▌    █▌   
█▌     █▌     █▌  █▌ █▌  █▌ █▌    █▌   
 ███▌  █▌     ████▌  █▌  █▌ █▌    █▌   
    █▌ █▌     █▌▐█   █▌  █▌ █▌    █▌   
█▌  █▌ █▌  █▌ █▌ ▐█  █▌  █▌ █▌    █▌   
 ███▌   ███▌  █▌  █▌  ███▌  █████ █████
-->

Taking Scroll Into Account {#scroll}
--------------------------

For performance reasons,
implementations usually perform scrolling on a separate scrolling/"compositing" thread,
which has very limited capabilities
(simple movement/transforms/etc., but no layout or similar expensive operations)
and thus can be relied upon to respond to scrolling
fast enough to be considered "instant" to human perception.

If scrolling just causes an anchor-positioned element to <em>move</em>,
there is in theory no issue;
the movement can be performed on the scrolling thread
so the positioned element moves smoothly with the scrolling content.
However, [=anchor positioning=] allows an element 
to make the positions of its own opposite edges
depend on things in <em>different</em> scrolling contexts,
which means scrolling could move just <em>one</em> edge and cause a size change,
and thus perform layout.
This can't be performed on the scrolling thread!

To compensate for this,
while still allowing as much freedom to anchor to various elements as possible,
[=anchor positioning=] uses a combination of [=remembered scroll offsets=]
and [=compensating for scroll=].

<div class=note>
	The details here are technical, but the gist is:

	* When a positioned element is first displayed, 
		or when it [[#fallback|changes fallbacks]],
		its position is correctly calculated
		according to the up-to-date position of all [=anchor references=].

		If these [=anchor references=] are in a different scroll context,
		their total scroll offsets are <em>memorized</em>,
		and layout will continue using those memorized offsets,
		even if those elements are scrolled later.
		(Only the scroll offsets are memorized;
		their actual laid-out positions are freshly calculated each time
		and remain accurate.)
		They'll only recalculate if the positioned element stops being displayed and starts again,
		or changes fallbacks.

	* The one exception to this is the [=default anchor element=];
		if it's scrolled away from its [=remembered scroll offset=],
		the positioned element moves with it.
		Because this is *purely* a shift in position,
		the positioned element can't change size
		or otherwise require layout in response.

	The end result is that [=anchor positioning=] should generally "just work",
	regardless of what the element is anchoring to,
	but it might be limited in how it can respond to scrolling.
</div>

<!--
Here's a silly little example of how anchor pos live-responds to layout changes,
even if they're in different scrolling contexts,
while ignoring the effects of scrolling:

<xmp>
<!DOCTYPE html>
<style>
@keyframes jiggle { from { height: 20px; } to { height: 50px; }}
.box {
	overflow:scroll; 
	border: thick dotted; 
	width: 100px; 
	height: 100px; 
	float: left;
	position: relative;
}
.jiggler {
	width: 50px; 
	background: green; 
	animation: jiggle 3s alternate-reverse infinite linear;
	margin-bottom: 200px;
}
.abspos {
	position: absolute; 
	background: blue; 
	inset: anchor(--left bottom) anchor(--right left) anchor(--right top) anchor(--left right);
}
</style>
<div class=box>
 <div class=jiggler style="anchor-name: --left;"></div>
</div>
<div class=box>
 <div class=jiggler style="position: absolute; bottom: 0; animation-duration: 2s; anchor-name: --right;"></div>
</div>
<div class=abspos></div>
</xmp>

-->

<div algorithm="scroll memory">
	An <dfn>anchor recalculation point</dfn>
	occurs for an [=absolutely positioned element=]
	whenever that element begins generating boxes
	(aka switches from ''display:none'' or ''display:contents''
	to any other 'display' value),
	identical to when it starts running CSS animations.

	An [=anchor recalculation point=] also occurs for an element
	when [=determining position fallback styles=] for that element;
	if it changes fallback styles as a result,
	it uses the result of the [=anchor recalculation point=]
	associated with the chosen set of fallback styles.

	When an [=anchor recalculation point=] occurs for an element |abspos|,
	then for every element |anchor|
	referenced by one of |abspos|'s [=anchor references=],
	it associates a <dfn>remembered scroll offset</dfn>
	equal to the <em>current</em> sum of the [=scroll offsets=]
	of all [=scroll container=] ancestors
	of |anchor|,
	up to but not including 
	|abspos|'s [=containing block=].
	The [=remembered scroll offset=] also accounts 
	for other scroll-dependent positioning changes,
	such as ''position: sticky''.
	If |abspos| has a [=default anchor element=],
	it always calculates a [=remembered scroll offset=] for it,
	even if |abspos| doesn't actually have an [=anchor reference=] to it.

	All [=anchor references=] are calculated
	as if all [=scroll containers=] were at their [=initial scroll position=],
	and then have their associated [=remembered scroll offset=]
	added to them.

	Issue: Transforms have the same issue as scrolling,
	so Anchor Positioning similarly doesn't pay attention to them normally.
	Can we go ahead and incorporate the effects of transforms here?
</div>

The above allows a positioned element to respond 
to the scroll positions of its [=anchor references=] <em>once</em>,
but if any of them are scrolled,
the positioned element will no longer appear to be anchored to them
(tho it will continue to respond to their non-scrolling movement).
While this problem can't be solved <em>in general</em>,
we <em>can</em> respond to the scrolling of <em>one</em> [=anchor reference=];
specifically, the [=default anchor element=]:

<div algorithm="compensate for scroll">
	An [=absolutely positioned box=] |abspos|
	<dfn export lt="compensate for scroll">compensates for scroll</dfn>
	in the horizontal or vertical axis
	if both of the following conditions are true:

	* |abspos| has a [=default anchor box=].

	* |abspos| has an [=anchor reference=] to its [=default anchor box=]
		or at least to something in the same scrolling context,
		aka at least one of:

		* |abspos|'s used [=self-alignment property=] value
			in that axis is ''anchor-center'';
		* |abspos| has a non-''position-area/none'' value for 'position-area'
		* at least one ''anchor()'' function on |abspos|'s
			[=used value|used=] [=inset properties=] in the axis
			refers to a [=target anchor element=]
			with the same nearest [=scroll container=] ancestor
			as |abspos|'s [=default anchor box=].

	Note: If |abspos| has a [=position options list=],
	then whether it [=compensates for scroll=] in an axis
	is also affected by the applied fallback style.

	|abspos|'s <dfn>default scroll shift</dfn> is a pair of lengths
	for the horizontal and vertical axises, respectively.
	Each length is calculated as:

	* If |abspos| is [=compensating for scroll=] in the axis,
		then the length is the difference between the [=remembered scroll offset=]
		of the [=default anchor element=]
		and what its current [=remembered scroll offset=] would be
		if it were recalculated.

	* Otherwise, the length is 0.

	After layout has been performed for |abspos|,
	it is additionally shifted by the [=default scroll shift=],
	as if affected by a [=transform=]
	(before any other transforms).

	Issue: Define the precise timing of the snapshot:
	updated each frame,
	before style recalc.

	Issue: Similar to [=remembered scroll offset=],
	can we pay attention to transforms on the [=default anchor element=]?

	Note: While [=remembered scroll offsets=]
	affect the value of ''anchor()'' functions,
	[=default scroll shift=] directly shifts the element,
	<em>after</em> determining the value of its [=inset properties=],
	applying alignment,
	etc.
	This is <em>usually</em> indistinguishable,
	but cases like ''round(anchor(outside), 50px)'',
	which transform the [=default anchor element's=] position
	in a non-linear fashion,
	will expose the difference in behavior.
</div>


<!-- Big Text: Alignment

 ███▌  █▌    ████  ███▌  █    █▌ █     █ █████▌ █    █▌ █████▌
▐█ ▐█  █▌     ▐▌  █▌  █▌ █▌   █▌ ██   ██ █▌     █▌   █▌   █▌
█▌  █▌ █▌     ▐▌  █▌     ██▌  █▌ █▌█ █▐█ █▌     ██▌  █▌   █▌
█▌  █▌ █▌     ▐▌  █▌ ██▌ █▌▐█ █▌ █▌ █ ▐█ ████   █▌▐█ █▌   █▌
█████▌ █▌     ▐▌  █▌  █▌ █▌  ██▌ █▌   ▐█ █▌     █▌  ██▌   █▌
█▌  █▌ █▌     ▐▌  █▌  █▌ █▌   █▌ █▌   ▐█ █▌     █▌   █▌   █▌
█▌  █▌ █████ ████  ███▌  █▌   ▐▌ █▌   ▐█ █████▌ █▌   ▐▌   █▌
-->

Anchor-Based Alignment {#alignment}
======================

<h3 id=position-area-alignment>
Area-specific Default Alignment</h3>

When 'position-area' is not ''position-area/none'',
the [=used value=] of ''align-self/normal'' [=self-alignment=]
changes depending on the <<position-area>> value,
to align the box towards the anchor:

* If the only the center track in an axis is selected,
	the default alignment in that axis is ''align-self/center''.
* If all three tracks are selected,
	the default alignment in that axis is ''align-self/anchor-center''.
* Otherwise, the default alignment in that axis
	is toward the non-specified side track:
	if it's specifying the “start” track of its axis,
	the default alignment in that axis is ''align-self/end''; etc.

However, if only one [=inset property=] in the relevant axis is ''inset/auto'',
the default alignment is instead
towards the edge with the non-''inset/auto'' inset;
and this is an ''align-self/unsafe'' alignment.

Note: This single-''inset/auto'' behavior preserves the way
a single specified inset controls the position of an [=absolutely positioned=] box.

<div class=example>
	For example, assuming an English-equivalent writing mode (horizontal-tb, ltr),
	then the value ''span-x-start top'' resolves to
	the ''position-area/start'' region of the vertical axis,
	and the ''position-area/start'' and ''position-area/center'' regions of the horizontal axis,
	so the default alignments will be ''align-self: end''
	(making the box's bottom [=margin edge=] flush with the bottom of the ''position-area/top'' region)
	and ''justify-self: end''
	(making the box's end-side [=margin edge=] flush with the end side of the ''position-area/span-start'' region).
</div>

<div class=note>
	If the box overflows its [=inset-modified containing block=],
	but would still fit within its [=original containing block=],
	by default it will “shift” to stay within its [=original containing block=],
	even if that violates its normal alignment.
	See [[css-align-3#overflow-values]] for details.

	This behavior makes it more likely
	that positioned boxes remain visible
	and within their intended bounds,
	even when their [=containing block=]
	ends up smaller than anticipated.

	For example, a ''position-area: bottom span-right'' value
	lets the positioned box stretch
	from its anchor's left edge
	to its containing block's right edge,
	and left-aligns it in that space by default.
	But if the positioned box is larger than that space
	(such as if the anchor is very close to the right edge of the screen),
	it will shift leftwards to stay visible.
</div>


<!-- Big Text: a-center

 ███▌         ███▌  █████▌ █    █▌ █████▌ █████▌ ████▌
▐█ ▐█        █▌  █▌ █▌     █▌   █▌   █▌   █▌     █▌  █▌
█▌  █▌       █▌     █▌     ██▌  █▌   █▌   █▌     █▌  █▌
█▌  █▌ ████▌ █▌     ████   █▌▐█ █▌   █▌   ████   ████▌
█████▌       █▌     █▌     █▌  ██▌   █▌   █▌     █▌▐█
█▌  █▌       █▌  █▌ █▌     █▌   █▌   █▌   █▌     █▌ ▐█
█▌  █▌        ███▌  █████▌ █▌   ▐▌   █▌   █████▌ █▌  █▌
-->

Centering on the Anchor: the ''anchor-center'' alignment value {#anchor-center}
--------------------------------------------------------------

<pre class=propdef>
Name: justify-self, align-self, justify-items, align-items
New Values: anchor-center
</pre>

The [=self-alignment properties=] allow an [=absolutely positioned box=]
to align itself within the [=inset-modified containing block=].
The existing values,
plus carefully chosen [=inset properties=],
are usually enough for useful alignment,
but a common case for anchored positioning--
centering over the [=anchor box=]--
requires careful and somewhat complex set-up to achieve.

The new <dfn value for="justify-self, align-self, justify-items, align-items">anchor-center</dfn> value
makes this case extremely simple:
if the positioned box has a [=default anchor box=],
then it is centered (insofar as possible)
over the [=default anchor box=]
in the relevant axis.
Additionally:
* The [=scrollable containing block=] is used in place of the [=local containing block=]
	where applicable,
	so that the entire [=scrollable overflow area=] (typically) is available
	for positioning.
* The [=used value=] of any ''top/auto'' [=inset properties=]
	and ''margin/auto'' [=margin properties=]
	resolves to ''0''.

If the box is not [=absolutely positioned=],
or does not have a [=default anchor box=],
this value behaves as ''<self-position>/center''
and has no additional effect on how [=inset properties=] resolve.

Note: When using ''anchor-center'', by default
if the anchor is too close to the edge of the box's
[=original containing block=],
it will “shift” from being purely centered,
in order to remain within the [=original containing block=].
See [[css-align-3#overflow-values]] for more details.

<!-- Big Text: a-size()

 ███▌         ███▌  ████ █████▌ █████▌   ██ ██  
▐█ ▐█        █▌  █▌  ▐▌      ▐▌ █▌      █▌   ▐█ 
█▌  █▌       █▌      ▐▌     ▐▌  █▌     █▌     ▐█
█▌  █▌ ████▌  ███▌   ▐▌    █▌   ████   █▌     ▐█
█████▌           █▌  ▐▌   █     █▌     █▌     ▐█
█▌  █▌       █▌  █▌  ▐▌  █      █▌      █▌   ▐█ 
█▌  █▌        ███▌  ████ █████▌ █████▌   ██ ██
-->

Anchor-Based Sizing {#sizing}
===================

An [=absolutely positioned box=]
can use the <dfn>anchor-size()</dfn> function
in its [=sizing properties=]
to refer to the size of one or more [=anchor boxes=].
The ''anchor-size()'' function resolves to a <<length>>.
It is only allowed in the [=accepted @position-try properties=]
(and is otherwise invalid).

The ''anchor-size()'' Function {#anchor-size-fn}
------------------------------

<pre class="propdef partial">
Name: width, height, min-width, min-height, max-width, max-height, top, left, right, bottom, margin-top, margin-left, margin-right, margin-bottom 
New values:	<<anchor-size()>>
</pre>

<pre class=prod>
anchor-size() = anchor-size( [ <<anchor-name>> || <<anchor-size>> ]? , <<length-percentage>>? )
<dfn><<anchor-size>></dfn> = width | height | block | inline | self-block | self-inline
</pre>

The ''anchor-size()'' function is similar to ''anchor()'',
and takes the same arguments,
save that the <<anchor-side>> keywords are replaced with <<anchor-size>>,
referring to the distance between two opposing sides.

The physical <<anchor-size>> keywords
(<dfn value for=anchor-size()>width</dfn>
and <dfn value for=anchor-size()>height</dfn>)
refer to the width and height,
respectively,
of the [=target anchor element=].
Unlike ''anchor()'', there is no restriction on having to match axises;
for example, ''width: anchor-size(--foo height);'' is valid.

The logical <<anchor-size>> keywords
(<dfn value for=anchor-size()>block</dfn>,
<dfn value for=anchor-size()>inline</dfn>,
<dfn value for=anchor-size()>self-block</dfn>,
and <dfn value for=anchor-size()>self-inline</dfn>)
map to one of the physical keywords
according to either the [=writing mode=] of the box
(for ''self-block'' and ''self-inline'')
or the [=writing mode=] of the box's [=containing block=]
(for ''anchor-size()/block'' and ''anchor-size()/inline'').

If the <<anchor-size>> keyword is omitted,
it defaults to behaving as whatever keyword
matches the axis of the property that ''anchor-size()'' is used in.
(For example, ''width: anchor-size()''
is equivalent to ''width: anchor-size(width)''.)

An ''anchor-size()'' function representing a [=resolvable anchor-size function=]
resolves at [=computed value=] time
(via [=style & layout interleaving=])
to the <<length>> separating the relevant edges
(either left and right, or top and bottom,
whichever is in the specified axis)
of the [=target anchor element's=] [=anchor box=].


<h4 id=anchor-size-resolution oldids=anchor-size-valid>
Resolution of ''anchor-size()''</h4>

An ''anchor-size()'' function is a
<dfn lt="resolvable anchor-size function|unresolvable anchor-size function">resolvable anchor-size function</dfn>
only if all the following conditions are true:

* It's applied to an [=absolutely positioned box=].
* There is a [=target anchor element=]
	for the box it's used on,
	and the <<anchor-name>> value specified in the function.

If any of these conditions are false,
the ''anchor-size()'' function resolves to its specified fallback value.
If no fallback value is specified,
it makes the declaration referencing it [=invalid at computed-value time=].


<!-- Big Text: fallback

█████▌  ███▌  █▌    █▌    ████▌   ███▌   ███▌  █▌  █▌
█▌     ▐█ ▐█  █▌    █▌    █▌  █▌ ▐█ ▐█  █▌  █▌ █▌ █▌ 
█▌     █▌  █▌ █▌    █▌    █▌  █▌ █▌  █▌ █▌     █▌█▌  
████   █▌  █▌ █▌    █▌    █████  █▌  █▌ █▌     ██    
█▌     █████▌ █▌    █▌    █▌  █▌ █████▌ █▌     █▌█▌  
█▌     █▌  █▌ █▌    █▌    █▌  █▌ █▌  █▌ █▌  █▌ █▌ █▌ 
█▌     █▌  █▌ █████ █████ ████▌  █▌  █▌  ███▌  █▌  █▌
-->

Overflow Management {#fallback}
===========================

Anchor positioning,
while powerful,
can also be unpredictable.
The [=anchor box=] might be anywhere on the page,
so positioning a box in any particular fashion
(such as above the anchor, or the right of the anchor)
might result in the positioned box overflowing its [=containing block=]
or being positioned partially off screen.

To ameliorate this, an [=absolutely positioned=] box
can use the 'position-try-fallbacks' property
to refer to several variant sets of positioning/alignment properties
(generated from the box's existing styles,
or specified in ''@position-try'' rules)
that the UA can try if the box overflows its initial position.
Each is applied to the box, one by one,
and the first that doesn't cause the box
to overflow its [=containing block=]
is taken as the winner.

'position-try-order' allows these options
to additional be sorted by the available space they define,
if it's more important for the box to have as much space as possible
rather than strictly follow some declared order.

Giving Fallback Options: the 'position-try-fallbacks' property {#position-try-fallbacks}
--------------------------------

<pre class=propdef>
Name: position-try-fallbacks
Value: none | [ [<<dashed-ident>> || <<try-tactic>>] | <<position-area>> ]#
Initial: none
Inherited: no
Applies to: [=absolutely positioned boxes=]
Animation type: discrete
</pre>

This property provides a list of alternate positioning styles
to try when the [=absolutely positioned box=]
overflows its [=inset-modified containing block=].
This <dfn export>position options list</dfn>
initially contains a single [=position option=]
generated from the element's base styles.

Each comma-separated entry in the list is a separate option:
either the name of a ''@position-try'' block,
or a <<try-tactic>> representing an automatic transformation of the box's existing computed style.

Values have the following meanings:

<dl dfn-type=value dfn-for=position-try-fallbacks>
	: <dfn>none</dfn>
	::
		The property has no effect;
		the box's [=position options list=] is empty.

	: <dfn><<dashed-ident>></dfn>
	::
		If there is a ''@position-try'' rule
		with the given name,
		its associated [=position option=]
		is added to the [=position options list=].

		Otherwise,
		this value has no effect.

	: <dfn><<try-tactic>></dfn>
	::
		Automatically creates a [=position option=]
		from the box's computed style,
		by [=swapping due to a try-tactic=]
		according to the specified keyword,
		and adding the constructed [=position option=]
		to the box's [=position options list=].
		and adds it to the [=position options list=].

		<pre class=prod>
		<dfn type><<try-tactic>></dfn> = flip-block || flip-inline || flip-start || flip-x || flip-y
		</pre>

		: <dfn>flip-block</dfn>
		::
			swaps the values in the [=block axis=]
			(between, for example, 'margin-block-start' and 'margin-block-end'),
			essentially mirroring across an [=inline-axis=] line.

		: <dfn>flip-inline</dfn>
		::
			swaps the values in the [=inline axis=],
			essentially mirroring across a [=block-axis=] line.

		: <dfn>flip-x</dfn>
		::
			swaps the values in the [=horizontal axis=]
			(between, for example, 'margin-left' and 'margin-right'),
			essentially mirroring across a [=vertical-axis=] line.

		: <dfn>flip-y</dfn>
		::
			swaps the values in the [=vertical axis=],
			essentially mirroring across a [=horizontal-axis=] line.

		: <dfn>flip-start</dfn>
		::
			swaps the values of the [=start=] properties with each other,
			and the [=end=] properties with each other
			(between, for example,
			'margin-block-start' and 'margin-inline-start'),
			essentially mirroring across a diagonal drawn
			from the [=block-start|start=]-[=inline-start|start=] corner
			to the [=block-end|end=]-[=inline-end|end=] corner.

		If multiple keywords are given,
		the transformations are composed in order
		to produce a single [=position option=].
		Logical directions are resolved against
		the [=writing mode=] of the [=containing block=].

	: <dfn><<dashed-ident>> || <<try-tactic>></dfn>
	::
		Combines the effects of the previous two options:
		if there is a ''@position-try'' rule
		with the given name,
		applies its [=position option=] to the base style,
		then transforms it according to the specified <<try-tactic>>
		and adds the result
		to the box's [=position options list=].

		Otherwise, does nothing.

	: <dfn><<position-area>></dfn>
	::
		Automatically creates a [=position option=]
		composed solely of a 'position-area' property
		with the given value.
</dl>

<div algorithm>
	To <dfn>swap due to a try-tactic</dfn>
	the styles of a box's element |el|
	between two directions |directions|,
	returning a set of styles:

	0. If |directions| are opposites along the same axis,
		they are “opposing”.
		Otherwise (when they are specifying different axises),
		they are “perpendicular”.

	1. Determine the specified values of the [=accepted @position-try properties=]
		on |el|,
		and let |styles| be the result.

	2. [=substitute a var()|Substitute variables=],
		''env()'' functions,
		and similar [=arbitrary substitution functions=]
		in |styles|.

		For ''env()'' functions, 
		if the referenced [=CSS/environment variable=] is associated with a direction or axis
		(such as ''env()/safe-area-inset-top''),
		switch the referenced [=CSS/environment variable=] corresponding to |directions|.

		<div class=example>
			For example, if ''top: env(safe-area-inset-top);'' is specified,
			and |directions| are up and left,
			the ''env()'' will resolve as if ''env(safe-area-inset-left)'' had been specified instead.
			(And then, in the next step, will actually swap into the 'left' property.)
		</div>

	3. Swap the values of the |styles| between
		the associated properties
		corresponding to |directions|.

		<div class=example>
			For example, if "top" and "left" are being swapped,
			then the values of 'margin-top' and 'margin-left' are swapped,
			'width' and 'height' are swapped,
			etc.
		</div>

		Note: If the directions are opposites along the same axis,
		some properties (like 'width' or 'align-self')
		wont' swap,
		since they're associated with themselves across the two directions,
		but their values might be changed by the next step.

	4. Modify the values of the properties
		as they swap to match the new directions,
		as follows:

		* For [=inset properties=],
			change the specified side in ''anchor()'' functions
			to maintain the same relative relationship to the new direction
			that they had to the old.

			If a <<percentage>> is used,
			and |directions| are opposing,
			change it to ''100%'' minus the original percentage.

			<div class=example>
				For example, if "top" and "left" are being swapped,
				then ''margin-top: anchor(bottom)''
				will become ''margin-left: anchor(right)''.

				If "top" and "bottom" are being swapped,
				then ''margin-top: anchor(20%)''
				will become ''margin-bottom: anchor(80%)''.
			</div>
		* For [=sizing properties=],
			change the specified axis in ''anchor-size()'' functions
			to maintain the same relative relationship to the new direction
			that they had to the old.

			<div class=example>
				For example, if "top" and "left" are being swapped,
				then ''width: anchor-size(width)''
				will become ''height: anchor-size(height)''.
			</div>
		* For the [=self-alignment properties=],
			if |directions| are opposing,
			change the specified <<self-position>>
			(or ''align-self/left''/''align-self/right'' keywords),
			if any,
			to maintain the same relative relationship to the new direction
			that they had to the old.

			<div class=example>
				For example, if "top" and "bottom" are being swapped,
				then ''align-self: start''
				will become ''align-self: end''.

				However, ''align-self: center'' will remain unchanged,
				as it has the same relationship to both directions.

				Similarly, ''align-self: first baseline'' will remain unchanged,
				as it's a <<baseline-position>>
				rather than a <<self-position>>.
			</div>
		* For 'position-area',
			change the value
			so that the selected rows/columns of the [=position-area grid=]
			maintain the same relative relationship to the new direction
			that they had to the old.

			<div class=example>
				For example, if "top" and "left" are being swapped,
				then ''position-area: left span-bottom''
				will become ''position-area: top span-right''.
			</div>

	5. Return |styles|.
</div>

<!-- Big Text: *-order

               ███▌  ████▌  ████▌  █████▌ ████▌ 
 █   █        █▌  █▌ █▌  █▌ █▌  █▌ █▌     █▌  █▌
  █ █         █▌  █▌ █▌  █▌ █▌  █▌ █▌     █▌  █▌
███████ ████▌ █▌  █▌ ████▌  █▌  █▌ ████   ████▌ 
  █ █         █▌  █▌ █▌▐█   █▌  █▌ █▌     █▌▐█  
 █   █        █▌  █▌ █▌ ▐█  █▌  █▌ █▌     █▌ ▐█ 
               ███▌  █▌  █▌ ████▌  █████▌ █▌  █▌
-->

Determining Fallback Order: the 'position-try-order' property {#position-try-order-property}
----------------------------------

<pre class=propdef>
Name: position-try-order
Value: normal | <<try-size>>
Initial: normal
Applies to: [=absolutely positioned boxes=]
Inherited: no
Animation Type: discrete
</pre>

This property specifies the order in which
the [=position options list=] will be tried.

<pre class=prod>
<dfn type><<try-size>></dfn> = most-width | most-height | most-block-size | most-inline-size
</pre>

<dl dfn-type=value dfn-for=position-try-order>
	: <dfn>normal</dfn>
	::
		Try the [=position options=]
		in the order specified by 'position-try-fallbacks'.

	: <dfn>most-width</dfn>
	: <dfn>most-height</dfn>
	: <dfn>most-block-size</dfn>
	: <dfn>most-inline-size</dfn>
	::
		For each entry in the [=position options list=],
		[=apply a position option|apply that position option=] to the box,
		and find the specified [=inset-modified containing block=] size
		that results from those styles.
		Stably sort the [=position options list=]
		according to this size,
		with the largest coming first.
</dl>

<div class=example>
	For example, the following styles
	will initially position the popup list below its anchoring button,
	but if that overflows,
	will decide whether to keep the popup list below the anchor
	or move it above,
	depending on which option gives it the most space.

	<pre highlight=css>
	.anchor { anchor-name: --foo; }
	.list {
		position: fixed;
		position-anchor: --foo;
		position-area: block-end span-inline-end;
		align-self: start;
		position-try-fallbacks: --bottom-scrollable, flip-block, --top-scrollable;
		position-try-order: most-height;
	}
	@position-try --bottom-scrollable {
		align-self: stretch;
	}
	@position-try --top-scrollable {
		position-area: block-start span-inline-end;
		align-self: stretch;
	}
	</pre>

	The ''flip-block'' auto-generated option and the ''--top-scrollable'' option
	will always find the same available height,
	since both of them stretch vertically from the top edge of the containing block
	to the top of the anchor,
	so they'll retain their specified order.
	This causes the box to first try to align against the anchor
	at its natural height
	(using ''align-self: end'', auto-reversed from the base styles),
	but if that also causes overflow,
	it'll fall back to just filling the space
	and being scrollable instead.
</div>

<!--
Final Fallback Strategy: the 'position-try-final' property {#position-try-final-property}
----------------------

<pre class=propdef>
Name: position-try-final
Value: [ always? && [ first | <<try-size>> ] ] | hide
Initial: first
Applies to: [=absolutely positioned boxes=]
Inherited: no
Animation Type: discrete
</pre>

When all options in the [=position options list=]
would result in the box overflowing its [=inset-modified containing block=],
this property determines which [=position option=] to use.

<dl dfn-type=value dfn-for=position-try-final>
	: <dfn>first</dfn>
	::
		Use the first option in the [=position options list=]
		(after sorting by 'position-try-order').

	: <dfn lt="<try-size> | most-width | most-height | most-block-size | most-inline-size"><<try-size>></dfn>
	::
		Stable sort the [=position options list=] as if this was the specified 'position-try-order',
		then use the first option.

	: <dfn>always</dfn>
	::
		If this keyword is <em>not</em> specified,
		and this box has previously been laid out
		with a [=position option=]
		that didn't result in overflow,
		use that option.

		Note: In other words, if all of the options cause the box to overflow,
		then by default
		the box will stay with the last successful [=position option=] it found
		that <em>didn't</em> cause overflow,
		keeping its position reasonably stable.
		With ''always'' specified, however,
		it will instead use the specified strategy
		to select the "fallback" [=position option=],
		regardless of what was previously successful.

	: <dfn>hide</dfn>
	::
		Use the first [=position option=] (as for ''first''),
		but also hide the box such that it (and all of its contents)
		are invisible (like ''visibility: hidden'')
		and do not contribute to [=scrollable overflow=].
</dl>
-->

The 'position-try' Shorthand {#position-try-prop}
-------------------------------------------------

<pre class="propdef shorthand">
Name: position-try
Value: <<'position-try-order'>>? <<'position-try-fallbacks'>>
</pre>

This shorthand sets both 'position-try-fallbacks' and 'position-try-order'.
If <<'position-try-order'>> is omitted,
it's set to the property's initial value.


<!-- Big Text: @pos-try

 ████▌  ████▌   ███▌   ███▌        █████▌ ████▌  █   ▐▌
█▌   █▌ █▌  █▌ █▌  █▌ █▌  █▌         █▌   █▌  █▌ ▐▌  █ 
█▌▐█ █▌ █▌  █▌ █▌  █▌ █▌             █▌   █▌  █▌  █ ▐▌ 
█▌▐█ █▌ ████▌  █▌  █▌  ███▌  ████▌   █▌   ████▌   ▐▌█  
█▌ ██▌  █▌     █▌  █▌     █▌         █▌   █▌▐█     █▌  
█▌      █▌     █▌  █▌ █▌  █▌         █▌   █▌ ▐█    █▌  
 ████▌  █▌      ███▌   ███▌          █▌   █▌  █▌   █▌  
-->

The ''@position-try'' Rule {#fallback-rule}
-------------------------------

The <dfn>@position-try</dfn> rule
defines a <dfn>position option</dfn>
with a given name,
specifying one or more sets of positioning properties
that can be applied to a box
via 'position-try-fallbacks',

The syntax of the ''@position-try'' rule is:

<pre class=prod>
@position-try <<dashed-ident>> {
	<<declaration-list>>
}
</pre>

The <<dashed-ident>> specified in the prelude
is the rule's name.
If multiple ''@position-try'' rules are declared with the same name,
they [=cascade=] the same as ''@keyframe'' rules do.

The ''@position-try'' rule only <dfn export lt="accepted @position-try properties">accepts</dfn>
the following [=properties=]:

* [=inset properties=]
* [=margin properties=]
* [=sizing properties=]
* [=self-alignment properties=]
* 'position-anchor'
* 'position-area'

It is invalid to use ''!important'' on the properties in the <<declaration-list>>.
Doing so causes the property it is used on to become invalid,
but does not invalidate the ''@property-try'' rule as a whole.

All of the properties in a ''@position-try'' are applied to the box
as part of the <dfn>Position Fallback Origin</dfn>,
a new [=cascade origin=] that lies between
the [=Author Origin=]
and the [=Animation Origin=].

Similar to the [=Animation Origin=],
use of the ''revert'' value
acts as if the property was part of the [=Author Origin=],
so that it instead reverts back to the [=User Origin=].
(As with the [=Animation Origin=], however,
''revert-layer'' has no special behavior
and acts as specified.)

Note: The [=accepted @position-try properties=] are the smallest group of properties
that affect just the size and position of the box itself,
without otherwise changing its contents or styling.
This significantly simplifies the implementation of position fallback
while addressing the fundamental need to move an anchor-positioned box
in response to available space.
Since these rules override normal declarations in the [=Author Origin=],
this also limits the poor interactions of ''@position-try'' declarations
with the normal cascading and inheritance of other properties.
It is expected that a future extension to [=container queries=]
will allow querying an element
based on the position fallback it's using,
enabling the sort of conditional styling
not allowed by this restricted list.

Note: If multiple boxes using different anchors
want to use the same fallback positioning,
just relative to their own anchor elements,
omit the <<anchor-name>> in ''anchor()''
and specify each box's anchor in 'position-anchor' instead.

Note: The most common types of fallback positioning
(putting the positioned box on one side of the anchor normally,
but flipping to the opposite side if needed)
can be done automatically
with keywords in 'position-try-fallbacks',
without using ''@position-try'' at all.



<!-- Big Text: apply

 ███▌  ████▌  ████▌  █▌    █   ▐▌
▐█ ▐█  █▌  █▌ █▌  █▌ █▌    ▐▌  █ 
█▌  █▌ █▌  █▌ █▌  █▌ █▌     █ ▐▌ 
█▌  █▌ ████▌  ████▌  █▌     ▐▌█  
█████▌ █▌     █▌     █▌      █▌  
█▌  █▌ █▌     █▌     █▌      █▌  
█▌  █▌ █▌     █▌     █████   █▌  
-->

Applying Position Fallback {#fallback-apply}
--------------------------

When a positioned box
(shifted by its [=default scroll shift=])
overflows its [=inset-modified containing block=],
and has more than one [=position option=] in its [=position options list=],
it [=determines position fallback styles=]
to attempt to find an option that avoids overflow.

These modified styles are applied to the element via [=interleaving=],
so they affect [=computed values=]
(and can trigger transitions/etc)
even though they depend on layout and [=used values=].

ISSUE: Rewrite this section to be less confusing and more correct.
See <a href="https://github.com/w3c/csswg-drafts/issues/12818">Issue 12818</a>,
<a href="https://github.com/w3c/csswg-drafts/issues/12890">Issue 12890</a>.

<div algorithm>
	To <dfn>apply a position option</dfn> to a box's element |el|,
	given a [=position option=] |new styles|:

	1. With |new styles| inserted into the cascade
		in the [=position fallback origin=],
		resolve the cascade,
		and perform enough layout to determine |el|'s [=used value|used styles=].

		For the purpose of calculating these styles,
		a <em>hypothetical</em> [=anchor recalculation point=] is calculated,
		and the resulting hypothetical [=remembered scroll offsets=] are used to determine |el|'s styles.

	2. Return |el|'s [=used value|used styles=].
</div>

<div algorithm="determine position fallback styles">
	To <dfn>determine position fallback styles</dfn> for an element |abspos|:

	1. Let |current styles| be the current used styles of |abspos|
		(which might be the result of earlier fallback).

	2. [=list/For each=] |option| in the [=position options list=]:

		1. If |option| is currently |abspos|'s [=last successful position option=],
			[=iteration/continue=].

		2. Let |adjusted styles| be the result of [=applying a position option=] |option| to |abspos|.

		3. Let |el rect| be the size and position of |abspos|'s margin box,
			and |cb rect| be the size and position of |abspos|'s [=inset-modified containing block=],
			when laid out with |adjusted styles|.

		4. If |cb rect| was negative-size in either axis
			and corrected into zero-size,
			[=iteration/continue=].

			Note: This prevents a zero-size |el rect|
			from still being considered "inside"
			a negative-size |cb rect|
			and getting selected as a successful option.

		5. If |el rect| is not fully contained within |cb rect|,
			[=iteration/continue=].

		6. Return |adjusted styles|,
			along with the associated set of [=remembered scroll offsets=]
			that were hypothetically calculated for them.

	3. Assert: The previous step finished without finding a [=position option=]
		that avoids overflow.

	4. Return |current styles|.

	Note: Descendants overflowing |el|
	don't affect this calculation,
	only |el|'s own [=margin box=].

	Note: Because we purposely skip the [=position option=] currently in effect,
	it doesn't get its [=remembered scroll offsets=] updated;
	if none of the other fallbacks work
	and we stick with the current styles,
	all the [=remembered scroll offsets=] stay the same.
</div>

During a full layout pass,
once a box has determined its fallback styles
(or determined it's not using any),
laying out later boxes cannot change this decision.

<div class=example>
	For example, say you have two positioned boxes,
	A and B,
	with A laid out before B.
	If B overflows and causes A's containing block to gain scrollbars,
	this <em>does not</em> cause A
	to go back and re-determine its fallback styles
	in an attempt to avoid overflowing.
	(At best, this can result in exponential layout costs;
	at worst, it's cyclic and will never settle.)

	Layout does not "go backward", in other words.
</div>

<div algorithm="remember or forget the last successful position option">
	At the time that {{ResizeObserver}} events are determined and delivered:

	* If |el| has a [=last successful position option=]
		remove its [=last successful position option=]
		if any of the following are true:

		* Its computed 'position' value has changed,
			its [=containing block=] association has changed,
			or it no longer generates a box.
		* Its computed value for any [=longhand property|longhand=] of 'position-try' has changed.
		* Its computed value for any [=accepted @position-try property=] has changed.
		* Any of the ''@position-try'' rules referenced by it
			have been added, removed, or mutated.

		Then, [=determine position fallback styles=] for |el|
		and set its [=last successful position option=]
		to the set of [=accepted @position-try properties=] (and values)
		that it's now using.

	* Otherwise, if a box |el| is [=absolutely positioned=],
		set its <dfn export>last successful position option</dfn>
		to the set of [=accepted @position-try properties=] (and values)
		that it's currently using.

	Note: The timing of this recording/removal
	is intentionally identical to the treatment of [=last remembered sizes=].
</div>

Implementations may choose to impose an implementation-defined limit
on the length of [=position options lists=],
to limit the amount of excess layout work that may be required.
This limit must be <em>at least</em> five.

<div class=example>
	For example,
	the following CSS will first attempt to position a "popover"
	below the element,
	but if it doesn't fit on-screen will switch to being above;
	it defaults to left-aligning,
	but will switch to right-aligning if that doesn't fit.

	<pre highlight=css>
	#myPopover {
		position: fixed;
		top: anchor(--button bottom);
		left: anchor(--button left);
		position-try-fallbacks: flip-inline, flip-block, flip-block flip-inline;

		/* The popover is at least as wide as the button */
		min-width: anchor-size(--button width);

		/* The popover is at least as tall as 2 menu items */
		min-height: 6em;
	}
	</pre>
</div>



<!-- Big Text: visibility

█▌   █▌ ████  ███▌  ████ ████▌  ████ █▌    ████ █████▌ █   ▐▌
█▌   █▌  ▐▌  █▌  █▌  ▐▌  █▌  █▌  ▐▌  █▌     ▐▌    █▌   ▐▌  █
█▌   █▌  ▐▌  █▌      ▐▌  █▌  █▌  ▐▌  █▌     ▐▌    █▌    █ ▐▌
▐▌   █   ▐▌   ███▌   ▐▌  █████   ▐▌  █▌     ▐▌    █▌    ▐▌█
 █  ▐▌   ▐▌      █▌  ▐▌  █▌  █▌  ▐▌  █▌     ▐▌    █▌     █▌
 ▐▌ █    ▐▌  █▌  █▌  ▐▌  █▌  █▌  ▐▌  █▌     ▐▌    █▌     █▌
  ▐█    ████  ███▌  ████ ████▌  ████ █████ ████   █▌     █▌
-->

Conditional Hiding: the 'position-visibility' property {#position-visibility}
------------------------------------------------------

<pre class=propdef>
Name: position-visibility
Value: always | [ anchors-valid || anchors-visible || no-overflow ]
Initial: anchors-visible
Applies to: [=absolutely positioned boxes=]
Percentages: n/a
Inherited: no
Animation type: discrete
</pre>

There are some conditions in which it might not make sense
to display an [=absolutely positioned box=].
This property allows such boxes to be made conditionally visible,
depending on some commonly needed layout conditions.

<dl dfn-type=value dfn-for=position-visibility>
	: <dfn>always</dfn>
	::
		This property has no effect.
		(The box is displayed without regard for its anchors
		or its overflowing status.)

	: <dfn>anchors-valid</dfn>
	::
		If any of the box's [=required anchor references=]
		do not resolve to a [=target anchor element=],
		the box's 'visibility' property computes to ''force-hidden''.

		Issue: What is a <dfn dfn for>required anchor reference</dfn>?
		''anchor()'' functions that don't have a fallback value;
		the default anchor *sometimes*?
		Need more detail here.

		Issue: <em>Any</em> anchors are missing,
		or <em>all</em> anchors are missing?
		I can see use-cases for either, potentially.
		Do we want to make a decision here, or make it controllable somehow?

	: <dfn>anchors-visible</dfn>
	::
		If the box has a [=default anchor box=]
		but that [=anchor box=] is [=invisible=] or [=clipped by intervening boxes=],
		the box's 'visibility' property computes to ''force-hidden''.

	: <dfn>no-overflow</dfn>
	::
		If the box overflows its [=inset-modified containing block=]
		even after applying 'position-try',
		the box's 'visibility' property computes to ''force-hidden''.
</dl>

<div algorithm>
	An anchor box |anchor|
	is <dfn noexport>clipped by intervening boxes</dfn>
	relative to a positioned box |abspos| relying on it
	if |anchor|'s [=ink overflow rectangle=]
	is fully clipped by a box
	which is an ancestor of |anchor|
	but a descendant of |abspos|'s containing block.
	Clipping in this case refers only to the same clipping effects
	that are (by default) checked by {{IntersectionObserver}},
	i.e. clipping due to 'clip-path',
	'overflow', or other effects (such as [=paint containment=])
	that clip to the [=overflow clip edge=].

	Whether or not |anchor| is [=clipped by intervening boxes=]
	must be checked after [=updating content relevancy for a document=]
	(see 'content-visibility' in [[css-contain-2]])
	and running any {{ResizeObserver}},
	but before running any {{IntersectionObserver}}.
	It may also be checked at other times to improve responsiveness.
	<!-- See https://github.com/w3c/csswg-drafts/issues/12732#issuecomment-3494097561 -->

	Note: This means that if an abspos is next to its anchor in the DOM,
	for example,
	it'll remain visible even if its default anchor is scrolled off,
	since it's clipped by the same scroller anyway.

	Issue: Make sure this definition of clipped
	is consistent with View Transitions,
	which wants a similar concept.

	Note: This ensures that in a “chained anchor” situation,
	if the first abspos is hidden due to this property
	(due to its anchor being scrolled off),
	then another abspos using it as an anchor will also be hidden,
	rather than <em>also</em> floating in a nonsensical location.
</div>

<!-- Big Text: anim

 ███▌  █    █▌ ████ █     █
▐█ ▐█  █▌   █▌  ▐▌  ██   ██
█▌  █▌ ██▌  █▌  ▐▌  █▌█ █▐█
█▌  █▌ █▌▐█ █▌  ▐▌  █▌ █ ▐█
█████▌ █▌  ██▌  ▐▌  █▌   ▐█
█▌  █▌ █▌   █▌  ▐▌  █▌   ▐█
█▌  █▌ █▌   ▐▌ ████ █▌   ▐█
-->
<!-- Deferred to level 2
Animating Position {#animating}
==================

<pre class=propdef>
Name: position-animation
Value: normal | magic
Initial: normal
Percentages: n/a
Inherited: no
Computed Value: ''position-animation/normal'', or an [=overriding position rectangle=] (see prose)
Animation type: see prose
Applies to: [=absolutely positioned boxes=]
</pre>

An [=absolutely positioned box=]'s position and size
are the result of multiple properties interacting,
and this interaction is non-linear,
so smoothly animating from one position to another
can't be accomplished by animating the individual properties independently.

The 'position-animation' property resolves this conundrum,
by representing the final result of applying these properties
as a <dfn>overriding position rectangle</dfn>:
a width, height, [=x-axis=] offset, and [=y-axis=] offset.
The width and height represent the size of its [=margin box=],
and the x and y offsets represent its position
relative to its [=containing block=],
after <a href="https://www.w3.org/TR/css-position/">positioning</a>/<a href="https://www.w3.org/TR/css-align/">alignment</a>/etc.
have been performed.

If the box is not [=absolutely positioned=],
this property has no effect.

Values are:

<dl dfn-type=value dfn-for=position-animation>
	: <dfn>normal</dfn>
	::
		This value has no effect.

	: <dfn>magic</dfn> (name to be bikeshedded)
	::
		At [=computed value=] time,
		resolves to a [=overriding position rectangle=],
		using [=interleaving=].

		At [=used value=] time,
		overrides the position and size of the box's [=margin box=],
		setting it to match the computed [=overriding position rectangle=].
</dl>

The ''position-animation/magic'' value always serializes as <css>magic</css>;
the [=overriding position rectangle=] is not observable in any way.

Interpolating to or from ''position-animation/normal''
is done as a discrete step,
where values of <var ignore>p</var> between 0 and 1 map to ''position-animation/normal'',
and other values of <var ignore>p</var> map to the closer endpoint.
<span class=note>(Similar to 'visibility'.)</span>

Two [=overriding position rectangles=] are interpolated
by interpolating the width, height, x offset, and y offset independently
as [=computed lengths=].

Issue: Should the x/y offset be relative to the top/left corner or (CB) center to (AbsPos) center or something else?

Note: The [=overriding position rectangle=] is not directly exposed,
because it functionally allows overriding the entire cascade;
the [=overriding position rectangle=] causes the box
to ignore all the other positioning properties,
regardless of where they come from.
The rectangle can nevertheless be <em>read</em> by authors
using existing JS APIs (such as {{Element/getBoundingClientRect()}}).

<div class=example>
	Given a change in positioning properties like:

	<pre highlight=css>
	p.start {
		top: 0px;
		bottom: 100px;
		align-self: start;
	}
	p.end {
		top: 100px;
		bottom: 50px;
		align-self: end;
	}
	</pre>

	The following causes a smooth animation between the two endpoints:

	<pre highlight=css>
	p {
		transition: position-animation 2s; /* magic transition yay */
		position-animation: magic;
	}
	</pre>

	This doesn't smoothly animate
	(you get the "constantly recomputed,
	triggering fresh transitions every frame" effect
	that occurs if you transition 'color' and 'border-color',
	using ''border-color:currentcolor''):

	<pre highlight=css>
	p {
		  transition: position-animation 2s, inset 2s; /* sadface */
	}
	</pre>
</div>

<div class=issue>
	Need a way to force <em>animations</em> to work correctly as well,
	like:

	<pre highlight=css>
	p {
		position-animation: magic;
		animation: foo 2s;
	}

	@keyframes foo {
		to {
			top: 0px;
			bottom: 100px;
			align-self: start;
		}
		from {
			top: 100px;
			bottom: 50px;
			align-self: end;
		}
	}
	</pre>

	Without some sort of magic intercept of animations
	(forcing them to turn into a 'position-animation' animation)
	this will not smoothly animate.
</div>
-->


<!-- Big Text: a11y 

 ███▌    █▌     █▌   █   ▐▌
▐█ ▐█   ██▌    ██▌   ▐▌  █ 
█▌  █▌ █ █▌   █ █▌    █ ▐▌ 
█▌  █▌   █▌     █▌    ▐▌█  
█████▌   █▌     █▌     █▌  
█▌  █▌   █▌     █▌     █▌  
█▌  █▌ █████▌ █████▌   █▌  
-->

Accessibility Implications {#accessibility}
==========

It's important to remember that Anchor Positioning
does not automatically establish any semantic relationship
between a positioned box
and any of its anchors,
because it can be used in many different ways.
Authors must not rely solely on a <em>visual</em> connection
implied by the positioning
to link elements together semantically;
without additional help,
the elements often have <em>no</em> meaningful DOM relationship,
making them difficult or impossible to use in non-visual user agents,
like screen readers.

Many features on the web platform,
both existing and upcoming,
allow establishing such connections explicitly,
so that non-visual user agents can also benefit.

For example, the [[HTML#the-popover-attribute|Popover API in HTML]]
automatically links the invoker button
to the popover element,
including automatically adjusting tabbing order;
it <em>also</em> establishes the invoker button
as the [=implicit anchor element=] for the popover,
making it easy to use Anchor Positioning as well.

Issue: Add a popover example.

In more general cases,
ARIA features such as
the <a href="https://w3c.github.io/aria/#aria-details"><code>aria-details</code></a>
or <a href="https://w3c.github.io/aria/#aria-describedby"><code>aria-describedby</code></a> attributes
on an anchor element
can provide this information
in a slightly more manual fashion;
in concert with the <{html-global/role}> attribute on the positioned element,
non-visual user agents
can tell their users about the relationship between the elements
and let them automatically navigate between them.



<!-- Big Text: cssom

 ███▌   ███▌   ███▌   ███▌  █     █
█▌  █▌ █▌  █▌ █▌  █▌ █▌  █▌ ██   ██
█▌     █▌     █▌     █▌  █▌ █▌█ █▐█
█▌      ███▌   ███▌  █▌  █▌ █▌ █ ▐█
█▌         █▌     █▌ █▌  █▌ █▌   ▐█
█▌  █▌ █▌  █▌ █▌  █▌ █▌  █▌ █▌   ▐█
 ███▌   ███▌   ███▌   ███▌  █▌   ▐█
-->

DOM Interfaces {#interfaces}
==========

The CSSPositionTryRule interface {#om-position-try}
-------------------------------------

The {{CSSPositionTryRule}} interface represents
the ''@position-try'' rule:

<xmp class='idl' export>
[Exposed=Window]
interface CSSPositionTryRule : CSSRule {
	readonly attribute CSSOMString name;
	[SameObject, PutForwards=cssText] readonly attribute CSSPositionTryDescriptors style;
};

[Exposed=Window]
interface CSSPositionTryDescriptors : CSSStyleDeclaration {
	attribute CSSOMString margin;
	attribute CSSOMString marginTop;
	attribute CSSOMString marginRight;
	attribute CSSOMString marginBottom;
	attribute CSSOMString marginLeft;
	attribute CSSOMString marginBlock;
	attribute CSSOMString marginBlockStart;
	attribute CSSOMString marginBlockEnd;
	attribute CSSOMString marginInline;
	attribute CSSOMString marginInlineStart;
	attribute CSSOMString marginInlineEnd;
	attribute CSSOMString margin-top;
	attribute CSSOMString margin-right;
	attribute CSSOMString margin-bottom;
	attribute CSSOMString margin-left;
	attribute CSSOMString margin-block;
	attribute CSSOMString margin-block-start;
	attribute CSSOMString margin-block-end;
	attribute CSSOMString margin-inline;
	attribute CSSOMString margin-inline-start;
	attribute CSSOMString margin-inline-end;
	attribute CSSOMString inset;
	attribute CSSOMString insetBlock;
	attribute CSSOMString insetBlockStart;
	attribute CSSOMString insetBlockEnd;
	attribute CSSOMString insetInline;
	attribute CSSOMString insetInlineStart;
	attribute CSSOMString insetInlineEnd;
	attribute CSSOMString top;
	attribute CSSOMString left;
	attribute CSSOMString right;
	attribute CSSOMString bottom;
	attribute CSSOMString inset-block;
	attribute CSSOMString inset-block-start;
	attribute CSSOMString inset-block-end;
	attribute CSSOMString inset-inline;
	attribute CSSOMString inset-inline-start;
	attribute CSSOMString inset-inline-end;
	attribute CSSOMString width;
	attribute CSSOMString minWidth;
	attribute CSSOMString maxWidth;
	attribute CSSOMString height;
	attribute CSSOMString minHeight;
	attribute CSSOMString maxHeight;
	attribute CSSOMString blockSize;
	attribute CSSOMString minBlockSize;
	attribute CSSOMString maxBlockSize;
	attribute CSSOMString inlineSize;
	attribute CSSOMString minInlineSize;
	attribute CSSOMString maxInlineSize;
	attribute CSSOMString min-width;
	attribute CSSOMString max-width;
	attribute CSSOMString min-height;
	attribute CSSOMString max-height;
	attribute CSSOMString block-size;
	attribute CSSOMString min-block-size;
	attribute CSSOMString max-block-size;
	attribute CSSOMString inline-size;
	attribute CSSOMString min-inline-size;
	attribute CSSOMString max-inline-size;
	attribute CSSOMString placeSelf;
	attribute CSSOMString alignSelf;
	attribute CSSOMString justifySelf;
	attribute CSSOMString place-self;
	attribute CSSOMString align-self;
	attribute CSSOMString justify-self;
	attribute CSSOMString positionAnchor;
	attribute CSSOMString position-anchor;
	attribute CSSOMString positionArea;
	attribute CSSOMString position-area;
};
</xmp>

Its <dfn attribute for=CSSPositionTryRule>name</dfn> attribute
represents the name declared in the rule's prelude.

Its <dfn attribute for=CSSPositionTryRule>style</dfn> attribute
represents the properties declared in the rule's body,
in the specified order.
On getting, it must return a {{CSSPositionTryDescriptors}} object
for the ''@position-try'' at-rule,
with the following properties:

: [=CSSStyleDeclaration/computed flag=]
:: Unset
: [=CSSStyleDeclaration/readonly flag=]
:: Unset
: [=CSSStyleDeclaration/declarations=]
:: The declared descriptors in the rule, in <l spec=cssom>[=specified order=]</l>.
: [=CSSStyleDeclaration/parent CSS rule=]
:: The context object
: [=CSSStyleDeclaration/owner node=]
:: Null

<h2 id=interleaving>
Appendix: Style & Layout Interleaving</h2>

<dfn export lt="style & layout interleave" local-lt="interleave">Style & layout interleaving</dfn> is a technique
where a style update can occur on a subtree
during the layout process,
resulting in retroactive updates
to elements’ [=computed value|computed styles=].

Issue: This is not the correct spec for this concept,
it should probably go in <a href="https://www.w3.org/TR/css-cascade/">Cascade</a>,
but I need a sketch of it to refer to.

Note: [=Style & layout interleaving=] is already used with [=container queries=]
and [=container query lengths=].
A length like ''10cqw'' is resolved into a [=computed length=]
using layout information about the query container's size,
which can thus trigger <a href="https://www.w3.org/TR/css-transitions-1/">transitions</a>
when the container changes size between layouts.

The [=accepted @position-try properties=] are also [=interleaved=]
when resolving fallback
(see 'position-try').

Issue: Obviously this needs way more details filled in,
but for now "act like you already do for container queries" suffices.
That behavior is also undefined,
but at least it's interoperable (to some extent?).


Security Considerations {#sec}
=======================

No Security issues have been raised against this document.

Privacy Considerations {#priv}
======================

No Privacy issues have been raised against this document.

Changes {#changes}
=======

Significant changes since the <a href="https://www.w3.org/TR/2025/WD-css-anchor-position-1-20251007/">7 October 2025 Working Draft</a>:

* Add ''flip-x'' and ''flip-y'' to 'position-try-fallbacks'.
	(<a href="https://github.com/w3c/csswg-drafts/issues/12869">Issue 12869</a>)
* Define ''anchor-center'' to also use the [=scrollable containing block=]
	so that it doesn't trigger overflow alignment when positioned outside the [=local containing block=].
	(<a href="https://github.com/w3c/csswg-drafts/issues/12952">Issue 12952</a>)
* Resolve ''margin/auto'' margins to zero when 'position-area' or 'anchor-center' is in effect,
	due to the ill-considered HTML UA default stylesheet rules for popovers.
	Also drop the <css>dialog</css> alignment value which was the previous attempt to address this problem.
	(<a href="https://github.com/w3c/csswg-drafts/issues/10258">Issue 10258</a>)
* Clarify that ''flip-block'', ''flip-inline'', and ''flip-start'' use the [=writing mode=] of the [=containing block=].
	(<a href="https://github.com/w3c/csswg-drafts/issues/12869">Issue 12869</a>)
* Add 'clip-path' to the list of clipping effects considered for ''anchors-visible''
	and clarify the timing of its checks.
	(<a href="https://github.com/w3c/csswg-drafts/issues/12732">Issue 12732</a>)
* Fix error where base styles were accidentally left out of the [=position options list=].
	(<a href="https://github.com/w3c/csswg-drafts/issues/12890">Issue 12890</a>)

See also <a href="https://www.w3.org/TR/2025/WD-css-anchor-position-1-20251007/#changes">Previous Changes</a>.
